Dart eval:Compiler 类
介绍
Compiler 类负责将 Dart 代码转换为 dart_eval 特色的 EVC 字节码。该类提供两个方法,compile 和 compileSource。
成员
ctx 根作用域:
var ctx = CompilerContext(0);
源码的列表:
final additionalSources = <DartSource>[];
内置 Eval 插件:
final plugins = <EvalPlugin>[
DartCorePlugin(),
DartMathPlugin(),
DartAsyncPlugin(),
];
其中:
- DartCorePlugin:提供部分 dart:core 能力(Future、Duration、DateTime)
- DartMathPlugin:提供部分 dart:math 能力
- DartAsyncPlugin:提供部分 dart:async 能力
compile 方法
传入一组 Dart 代码进行编译,是 compileSources 的封装。函数签名:
Program compile(Map<String, Map<String, String>> packages) {
final sources = packages
.entries
.expand((packageEntry) => packageEntry.value.entries
.map((library) => DartSource(
'package:${packageEntry.key}/${library.key}',
library.value)));
return compileSources(sources);
}
其中,入参示例:
/// {
/// 'package_name': {
/// 'file_name1.dart': '''code''',
/// 'file_name2.dart': '''code'''
/// }
/// }
按照包名-模块名-源代码 3 层维度。
具体的处理是:遍历包中每一个源代码,封装为 DartSource 对象:
- 第一个参数是包名+模块名
- 第二个参数是对应的源代码
_topLevelDeclarationsMap
签名:
var _topLevelDeclarationsMap = <int, Map<String, DeclarationOrBridge>>{};
顶层声明的映射列表:
- key 的 int 是 libraryIndex,也就是说双维度下的第一个维度是 Library
- value 是 Map,第二个维度是 Library 内的声明
- Key 是 String 声明的名称,有 dart_eval 定义的简单协议
- Value 是 DeclarationOrBridge:对应的 AST 语法节点
相关方法:
- compileSources 展开操作
compileSources 方法
该方法执行具体的编译工作。具体代码如下:
/// Compile a unit set of Dart code into a program
Program compileSources([Iterable<DartSource> sources = const [], bool debugPerf = true]) {
_topLevelDeclarationsMap = <int, Map<String, DeclarationOrBridge>>{};
_topLevelGlobalIndices = <int, Map<String, int>>{};
_instanceDeclarationsMap = <int, Map<String, Map<String, Declaration>>>{};
// 创建一个作用域
ctx = CompilerContext(0);
//……
// 遍历 sources(附加上 additionalSources)
// units 为所有代码编译后的抽象语法树,类型为 List<DartCompilationUnit>
final units = sources.followedBy(additionalSources).map((source) {
//……
//加载源代码,如果代码位于文件中,将读取磁盘
//在内部调用 dart analyzer 的 parseString 方法,将源码转换为抽象语法树,得到 CompilationUnit
//再对 CompilationUnit 进行解析,最后转化为 DartCompilationUnit 实体
final parsed = cachedParsedSources[source] = source.load();
return parsed;
}).toList();
//……
// 类型为 Set<Library>,将所有源码解析为 Library
// Library 为 dart_eval 提供的实体,表示一个 Library
// 在 _buildLibraries 中解析类之间的引用关系(后文分析)
final unitLibraries = {
..._buildLibraries(units),
};
// 建立 uri 到 Library 的映射关系,for 还能在 Map 中这么使用
final unitLibraryUriMap = {for (final library in unitLibraries) library.uri: library};
// 对源码 library 和桥接 library 的合并
final libraries = <Library>{};
// 记录同时提供源码和桥接的 library
final mergedLibraryUris = <Uri>{};
// 桥接库
for (final bridgeLibrary in _bridgeDeclarations.keys) {
/// 创建桥接库声明列表,List<DeclarationOrBridge>
final bridgeLibDeclarations = [
// _bridgeDeclarations 类型为 <String, List<BridgeDeclaration>>
// key 是对应的 library,value 是所有的声明
for (final bridgeDeclaration in _bridgeDeclarations[bridgeLibrary]!)
// DeclarationOrBridge 和 BridgeDeclaration 不是一个类
DeclarationOrBridge(-1, bridge: bridgeDeclaration)
];
/// 桥接库 url
final uri = Uri.parse(bridgeLibrary);
// 看传入的代码里面,有没有 bridgeLibrary
// 如果两者有重合,进行合并操作
final unitLibrary = unitLibraryUriMap[uri];
if (unitLibrary != null) { // 重合
/// declarations 中将源码实现与桥接实现合二为一
libraries.add(unitLibrary.copyWith(declarations: [...unitLibrary.declarations, ...bridgeLibDeclarations]));
/// 记录这是一个 mergedLibrary
mergedLibraryUris.add(uri);
} else {
// 如果两者没重合,根据 bridgeLibrary 创建一个 Library
libraries.add(Library(Uri.parse(bridgeLibrary), imports: [], exports: [], declarations: [
for (final bridgeDeclaration in _bridgeDeclarations[bridgeLibrary]!)
DeclarationOrBridge(-1, bridge: bridgeDeclaration)
]));
}
}
// 将纯源码 library 添加到 libraries 中
unitLibraryUriMap.forEach((uri, library) {
if (!mergedLibraryUris.contains(uri)) {
libraries.add(library);
}
});
var i = 0;
final libraryIndexMap = <Library, int>{};
// 解析库的 export 和 import 关系
// 通过 libraryIndexMap 记录了一个索引
final visibleDeclarations =
_resolveImportsAndExports(
libraries,
(library) => libraryIndexMap[library] ?? (libraryIndexMap[library] = i++));
// 又进行了一步操作,没太看懂
for (final library in libraries) {
final libraryIndex = libraryIndexMap[library] ?? (libraryIndexMap[library] = i++);
for (final declarationOrBridge in library.declarations) {
_populateLookupTablesForDeclaration(libraryIndex, declarationOrBridge);
}
}
// 将 library 和源码的映射传入作用域
final libraryMapString = {for (final entry in libraryIndexMap.entries) entry.key.uri.toString(): entry.value};
ctx.libraryMap = libraryMapString;
//
final visibleDeclarationsByIndex = {
for (final lib in libraries) libraryIndexMap[lib]!: {...visibleDeclarations[lib]!}
};
final declarationTypes = <DeclarationOrBridge, TypeRef>{};
for (final library in libraries) {
final libraryIndex = libraryIndexMap[library]!;
for (final declaration in library.declarations) {
final type = _cacheTypeRef(libraryIndex, declaration);
if (type != null) {
declarationTypes[declaration] = type;
}
}
}
final visibleTypesByIndex = <int, Map<String, TypeRef>>{};
for (final library in libraries) {
final libraryIndex = libraryIndexMap[library]!;
final declarations = visibleDeclarations[library]!;
for (final entry in declarations.entries) {
final name = entry.key;
final dop = entry.value;
if (dop.children != null) {
final res = <String, TypeRef>{};
for (final childName in dop.children!.keys) {
final child = dop.children![childName]!;
final _cached = declarationTypes[child];
if (_cached == null) continue;
res['$name.$childName'] = _cached;
if (child.isBridge) {
final bridge = child.bridge!;
if (bridge is BridgeClassDef) {
child.bridge =
bridge.copyWith(type: bridge.type.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[_cached])));
} else if (bridge is BridgeEnumDef) {
child.bridge = bridge.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[_cached]));
} else {
assert(false);
}
}
}
visibleTypesByIndex[libraryIndex] ??= {...coreDeclarations};
visibleTypesByIndex[libraryIndex]!.addAll(res);
continue;
}
visibleTypesByIndex[libraryIndex] ??= {...coreDeclarations};
final declarationOrBridge = dop.declaration!;
final type = declarationTypes[declarationOrBridge];
if (type == null) continue;
if (declarationOrBridge.isBridge) {
final bridge = declarationOrBridge.bridge!;
if (bridge is BridgeClassDef) {
declarationOrBridge.bridge =
bridge.copyWith(type: bridge.type.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[type])));
} else if (bridge is BridgeEnumDef) {
declarationOrBridge.bridge = bridge.copyWith(type: BridgeTypeRef.type(ctx.typeRefIndexMap[type]));
} else {
assert(false);
}
}
visibleTypesByIndex[libraryIndex]![name] = type;
}
}
ctx.topLevelDeclarationsMap = _topLevelDeclarationsMap;
ctx.instanceDeclarationsMap = _instanceDeclarationsMap;
ctx.visibleDeclarations = visibleDeclarationsByIndex;
ctx.visibleTypes = visibleTypesByIndex;
for (final library in libraries) {
final libraryIndex = libraryIndexMap[library]!;
for (final dec in library.declarations) {
if (dec.isBridge) {
final bridge = dec.bridge;
if (bridge is BridgeClassDef) {
_assignBridgeStaticFunctionIndicesForClass(bridge);
} else if (bridge is BridgeEnumDef) {
_assignBridgeGlobalValueIndicesForEnum(bridge);
} else if (bridge is BridgeFunctionDeclaration) {
_assignBridgeStaticFunctionIndicesForFunction(libraryIndex, bridge);
}
}
}
}
ctx.topLevelGlobalIndices = _topLevelGlobalIndices;
try {
/// Compile statics first so we can infer their type
_topLevelDeclarationsMap.forEach((key, value) {
value.forEach((lib, _declaration) {
if (_declaration.isBridge) {
return;
}
final declaration = _declaration.declaration!;
ctx.library = key;
if (declaration is VariableDeclaration && declaration.parent!.parent is TopLevelVariableDeclaration) {
compileDeclaration(declaration, ctx);
ctx.resetStack();
} else if (declaration is ClassDeclaration) {
ctx.currentClass = declaration;
for (final d in declaration.members.whereType<FieldDeclaration>().where((e) => e.isStatic)) {
compileFieldDeclaration(-1, d, ctx, declaration);
ctx.resetStack();
}
ctx.currentClass = null;
}
});
});
/// Compile the rest of the declarations
_topLevelDeclarationsMap.forEach((key, value) {
ctx.topLevelDeclarationPositions[key] = {};
ctx.instanceDeclarationPositions[key] = {};
value.forEach((lib, _declaration) {
if (_declaration.isBridge) {
return;
}
final declaration = _declaration.declaration!;
if (declaration is ConstructorDeclaration ||
declaration is MethodDeclaration ||
declaration is VariableDeclaration) {
return;
}
ctx.library = key;
compileDeclaration(declaration, ctx);
ctx.resetStack();
});
});
} on CompileError catch (e) {
throw e.copyWithContext(ctx);
}
for (final library in libraries) {
for (final dec in library.declarations) {
if (dec.isBridge) {
final bridge = dec.bridge;
if (bridge is BridgeClassDef && bridge.bridge) {
_reassignBridgeStaticFunctionIndicesForClass(bridge);
}
}
}
}
for (final type in ctx.runtimeTypeList) {
ctx.typeTypes.add(type.resolveTypeChain(ctx).getRuntimeIndices(ctx));
}
final globalInitializers = List<int>.filled(ctx.globalIndex, 0);
for (final gi in ctx.runtimeGlobalInitializerMap.entries) {
globalInitializers[gi.key] = gi.value;
}
return Program(
ctx.topLevelDeclarationPositions,
ctx.instanceDeclarationPositions,
ctx.typeNames,
ctx.typeTypes,
ctx.offsetTracker.apply(ctx.out),
libraryMapString,
ctx.bridgeStaticFunctionIndices,
ctx.constantPool.pool,
ctx.runtimeTypes.pool,
globalInitializers,
ctx.enumValueIndices);
}
_buildLibraries 方法
先看该方法的函数签名:
List<Library> _buildLibraries(Iterable<DartCompilationUnit> units) {
可以看出,它负责将一组 DartCompilationUnit 转换为一组 Library。
在《Dart eval:DartCompilationUnit 实体类》有介绍:该类表示一个经过解析后的 Dart 源文件(AST),但是尚未被解析为库(library)。
_buildLibraries 方法则完成源代码到 Library 的最终转化。
具体代码实现如下:
List<Library> _buildLibraries(Iterable<DartCompilationUnit> units) {
/// 自增的 id 生成器,每个 Library 都有一个唯一整型 Id 与之对应
var i = 0;
/// id-DartCompilationUnit 映射
final compilationUnitMap = <int, DartCompilationUnit>{};
/// url-id 映射
final uriMap = <String, int>{};
/// library-id 映射
final libraryIdMap = <String, int>{};
for (final unit in units) {
/// 建立映射关系
compilationUnitMap[i] = unit;
uriMap[unit.uri.toString()] = i;
if (unit.library != null) {
/// Library 指令,有的源码开头的 "library ******"
libraryIdMap[unit.library!.name.name] = i;
}
/// id 自增
i++;
}
// CompilationUnit 图结构
final cuGraph = CompilationUnitGraph(compilationUnitMap, uriMap, libraryIdMap);
// 计算强链接组件,基于 Dijkstra Path-based_strong_component_algorithm 算法
// 返回类型 List<List<T>>,内层列表应该是 id 列表
final libGroups = computeStrongComponents(cuGraph);
final libraries = <Library>[];
/// 第一层遍历针对有 part of 的场景,多个文件组成一个 Library
for (final group in libGroups) { /// 第一层 List 是 Group
// 找出主文件 ID
final primaryId = group.length == 1 ? group[0] : group.firstWhere((e) => compilationUnitMap[e]!.partOf == null);
// 主文件
final primary = compilationUnitMap[primaryId]!;
// 创建 Library
final library = Library(
primary.uri,
library: primary.library?.name.name,
imports: primary.imports,
exports: primary.exports,
declarations: group /// 将 group 中所有 declarations 转为 DeclarationOrBridge
.map((e) => compilationUnitMap[e]!)
.fold(
[], // 初始值
// pv:保存值,element:当前元素
// .. 是添加完元素再把 pv 返回去
(pv, element) => pv..addAll(
element.declarations.map(
(d) => DeclarationOrBridge(-1, declaration: d)))));
libraries.add(library);
}
return libraries;
}
如果把 Dijkstra 当作一个黑盒的话,整体逻辑并不复杂。作为黑盒的 Dijkstra 算法,主要是:有的 Dart 代码有多个文件构成(通过 part、part of 语法),这里将这种代码的 part 和主题汇集到一个 group 中去。
组后,该方法可以简单认为是,Dart 中源文件就是一个 Library,完成从 DartCompilationUnit 到 Library 的最终转化。
_expandDeclarations 方法
方法签名:
Iterable<Pair<String, DeclarationOrBridge>> _expandDeclarations(
List<DeclarationOrBridge> declarations) sync* {
是一个生成器方法,该方法传入的 declarations 是一个 Library 的 declarations。 该方法的作用是:declarations 表示声明,声明内部可能还有声明(如类声明内部包括成员声明),给方法将其全部解析打平,结果的 Pair 的 key 是打平后声明的唯一表示,Value 是对应的声明实体 DeclarationOrBridge。
Iterable<Pair<String, DeclarationOrBridge>> _expandDeclarations(List<DeclarationOrBridge> declarations) sync* {
/// 遍历声明
for (final d in declarations) {
if (d.isBridge) { /// 处理桥接声明
final bridge = d.bridge as BridgeDeclaration;
/// 只不过是把名字按照不同类型处理了一下,第二参数还是 DeclarationOrBridge
if (bridge is BridgeClassDef) { /// 声明一个类
/// 桥接类名称
final name = bridge.type.type.spec!.name;
yield Pair(name, d);
} else if (bridge is BridgeEnumDef) { /// 声明一个枚举
/// 桥接枚举名称
final name = bridge.type.spec!.name;
yield Pair(name, d);
} else if (bridge is BridgeFunctionDeclaration) { /// 声明一个桥接函数
/// 这个简单,直接就是函数名称
yield Pair(bridge.name, d);
}
} else { /// 如果是源码声明
final declaration = d.declaration!;
if (declaration is NamedCompilationUnitMember) {
final dName = declaration.name2.value() as String;
/// 先把当前发出去
yield Pair(dName, d);
/// 如果是类声明
if (declaration is ClassDeclaration) {
/// 再把类里面的成员也发出去
/// 类成员类型包括:ConstructorDeclaration、MethodDeclaration、MethodDeclaration
for (final member in declaration.members) {
if (member is ConstructorDeclaration) {
yield Pair('$dName.${member.name2?.value() ?? ""}', DeclarationOrBridge(-1, declaration: member));
} else if (member is MethodDeclaration && member.isStatic) {
yield Pair('$dName.${member.name2.value()}', DeclarationOrBridge(-1, declaration: member));
} else if (member is MethodDeclaration && member.isStatic) {
yield Pair('$dName.${member.name2.value()}', DeclarationOrBridge(-1, declaration: member));
}
}
}
} else if (declaration is TopLevelVariableDeclaration) { /// 顶层变量声明
for (final v in declaration.variables.variables) {
yield Pair(v.name2.value() as String, DeclarationOrBridge(-1, declaration: v));
}
}
}
}
}
_resolveImportsAndExports 方法
先看看方法签名:
Map<Library, Map<String, DeclarationOrPrefix>> _resolveImportsAndExports(
Iterable<Library> libraries,
int Function(Library) resolveLibraryId) {
传入参数:
- libraries 列表
- 一个函数,用于解析 library 的 id
返回值:一个 Map:
- key 是 library
- value 又是一个 Map,这里面的内容不太清晰
- key 是字符串
- value 是 DeclarationOrPrefix
外面实际调用时,传入的函数长这样:
(library) => libraryIndexMap[library] ?? (libraryIndexMap[library] = i++)
可以看到,在 compileSource 中,还有一个基于自增的整型 id 生成器。 下面来看内部具体逻辑:
Map<Library, Map<String, DeclarationOrPrefix>> _resolveImportsAndExports(
Iterable<Library> libraries,
int Function(Library) resolveLibraryId) {
/// uri-Library 映射
final uriMap = {for (final l in libraries) l.uri: l};
/// 基于 library 的 export 构成的有向图
final exportGraph = DirectedGraph<Uri>(
// 传入参数是个 Map,表示 edges
// 遍历 libraries,key 是 url,value 是一个 Set,是它对外的 export
{
for (final l in libraries) l.uri: {for (final export in l.exports) Uri.parse(export.uri.stringValue!)}
}
);
final result = <Library, Map<String, DeclarationOrPrefix>>{};
// 遍历 libraries
for (final l in libraries) {
// 该 Library 下所有的可见声明
final _visibleDeclarations = <String, DeclarationOrPrefix>{
for (final d in _expandDeclarations(l.declarations))
// Key:声明的标识 value:DeclarationOrPrefix(声明内容,并记忆对应 library 的 id)
d.first: DeclarationOrPrefix(declaration: d.second..sourceLib = resolveLibraryId(l)),
};
final dartCoreUri = Uri.parse('dart:core');
final isDartCore = l.uri == dartCoreUri;
for (final import in [
/// 取出库的所有 imports 做映射
...l.imports.map( /// e 类型是 ImportDirective
(e) => _Import( /// _Import 是一个类
Uri.parse(e.uri.stringValue!),
e.prefix?.name, /// as 某某
e.combinators)), /// show 某某 或者 hide 某某
if (!isDartCore) _Import(dartCoreUri, null) // DartCore 单独处理,如果有的话拼在后面
]) {
/// 使用了 directed_graph 库
/// 返回一个以 import.uri 为根的树状结构,类型为 List<Set<T>>
final tree = exportGraph.crawler.tree(import.uri);
///
final importedLibs = [...tree.expand((e) => e), import.uri]
.map((e) => uriMap[e] ?? (throw CompileError("Cannot find import '$e' (while parsing '${l.uri}')")))
.toSet();
final importedExports = importedLibs.map((e) => e.exports).expand((e) => e);
final exportsPerUri = <Uri, List<ExportDirective>>{};
for (final export in importedExports) {
final uriList = exportsPerUri[export.uri.stringValue!];
if (uriList != null) {
uriList.add(export);
} else {
exportsPerUri[Uri.parse(export.uri.stringValue!)] = [export];
}
}
final validImport = (String name) {
if (name.startsWith('_')) return false;
if (import.combinators.isEmpty) {
return true;
}
for (final combinator in import.combinators) {
if (combinator is ShowCombinator) {
if ({for (final n in combinator.shownNames) n.name}.contains(name)) {
return true;
}
return false;
} else if (combinator is HideCombinator) {
if ({for (final n in combinator.hiddenNames) n.name}.contains(name)) {
return false;
}
return true;
}
throw CompileError('Unsupported import combinator ${combinator.runtimeType} (while parsing ${l.uri})');
}
return false;
};
final visibleDeclarations = {
for (final lib in importedLibs)
for (final declaration in _expandDeclarations(lib.declarations).where((e) => validImport(e.first))) ...{
if (lib.uri == import.uri) declaration,
for (final export in exportsPerUri[lib.uri] ?? [])
if (export.combinators.isEmpty)
declaration
else
for (final combinator in export.combinators)
if (combinator is ShowCombinator) ...{
if ({for (final n in combinator.shownNames) n.name}.contains(declaration.first)) declaration
} else if (combinator is HideCombinator) ...{
if (!({for (final n in (combinator).hiddenNames) n.name}.contains(declaration.first))) declaration
}
}
};
final mappedVisibleDeclarations = {
if (import.prefix != null)
import.prefix!: DeclarationOrPrefix(children: {for (final d in visibleDeclarations) d.first: d.second})
else
for (final d in visibleDeclarations) d.first: DeclarationOrPrefix(declaration: d.second)
};
_visibleDeclarations.addAll(mappedVisibleDeclarations);
}
result[l] = _visibleDeclarations;
}
return result;
}